package com.gitee.ywj1352.server.handler;

import com.gitee.ywj1352.common.message.MessageHeader;
import com.gitee.ywj1352.common.message.RequestMessage;
import com.gitee.ywj1352.common.message.ResponseMessage;
import com.gitee.ywj1352.common.util.JSONUtils;
import com.gitee.ywj1352.common.util.ServerStructure;
import com.gitee.ywj1352.server.ServerFactory;
import com.sun.istack.internal.NotNull;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.URL;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class ServerProcessHandler extends SimpleChannelInboundHandler<RequestMessage> {


    private ServerFactory serverFactory;

    public ServerProcessHandler(ServerFactory serverFactory) {
        this.serverFactory = serverFactory;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RequestMessage requestMessage) throws Exception {
        if (ctx.channel().isActive() && ctx.channel().isWritable()) {
            MessageHeader messageHeader = requestMessage.getMessageHeader();
            URL url = messageHeader.getKey();
            String key = this.getParamFromQuery(url.getQuery(), "serverName");
            ServerStructure server = serverFactory.getServer(key);
            String methodKey = this.getParamFromQuery(url.getQuery(), "method");
            ServerStructure.MethodStructure methodStructure = server.getMap().get(methodKey);
            Object[] params = getParams(requestMessage.getMessageBody(), methodStructure.getParamsType());
            Object invoke = methodStructure.getMethod().invoke(server.getServerObj(), params);
            ResponseMessage responseMessage = new ResponseMessage();
            responseMessage.setMessageHeader(requestMessage.getMessageHeader());
            responseMessage.generate(new Class[]{methodStructure.getReturnClazz()}, new Object[]{invoke});
            ctx.writeAndFlush(responseMessage);
        } else {
            log.error("execute has error ");
        }
    }

    private String getParamFromQuery(String query, String key) {
        if (query == null || query.length() == 0) return null;
        Map<String, String> map = Stream.of(query.split("[&]")).map(s -> s.split("[=]")).collect(Collectors.toMap(arr -> arr[0], arr -> arr[1]));
        return map.get(key);
    }

    private Object[] getParams(Map<String, String> map, Class<?>[] paramTypeArr) {
        Object[] objArr = new Object[map.size()];
        int idx = 0;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String name = entry.getKey();
            String value = entry.getValue();
            objArr[idx] = getParam(name, paramTypeArr[idx], value);
            idx++;
        }
        return objArr;
    }

    private Object getParam(@NotNull String name, Class<?> clazz, String value) {
        switch (name) {
            case "java.lang.Integer":
                return Integer.valueOf(value);
            case "java.lang.Long":
                return Long.valueOf(value);
            case "java.lang.String":
                return value;
            default:
                return JSONUtils.deserialize(value, clazz);
        }
    }

}
